import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.util.List;

/**
 * 用于绘制有向图的自定义面板
 */
public class GraphPanel extends JPanel {
    private Map<String, Point> nodePositions = new HashMap<>();
    private Map<String, Set<TextGraphProcessor.Edge>> graph;
    private Map<String, Color> nodeColors = new HashMap<>();
    private static final int NODE_RADIUS = 28;
    private static final Color NODE_COLOR = new Color(100, 180, 255);
    private static final Color EDGE_COLOR = new Color(80, 80, 80);
    private static final Color TEXT_COLOR = new Color(0, 0, 0);
    private static final Stroke EDGE_STROKE = new BasicStroke(1.0f);
    private static final Stroke ARROW_STROKE = new BasicStroke(1.0f);
    private static final double SCALE_FACTOR = 2.2;
    
    public GraphPanel() {
        setBackground(Color.WHITE);
    }
    
    public void setGraph(Map<String, Set<TextGraphProcessor.Edge>> graph) {
        this.graph = graph;
        calculateNodePositions();
        repaint();
    }
    
    private void calculateNodePositions() {
        nodePositions.clear();
        
        if (graph == null || graph.isEmpty()) {
            return;
        }
        
        // 使用简化的力导向布局算法
        int nodeCount = graph.size();
        double radius = Math.min(getWidth(), getHeight()) * 0.3;
        
        // 初始布局：环形排列所有节点
        int i = 0;
        List<String> nodes = new ArrayList<>(graph.keySet());
        
        // 随机分配节点颜色
        Random rand = new Random(123); // 固定种子使颜色保持一致
        
        for (String node : nodes) {
            double angle = 2 * Math.PI * i / nodeCount;
            int x = (int) (getWidth() / 2 + radius * Math.cos(angle));
            int y = (int) (getHeight() / 2 + radius * Math.sin(angle));
            nodePositions.put(node, new Point(x, y));
            
            // 为节点分配随机颜色
            float h = rand.nextFloat();
            float s = 0.5f + rand.nextFloat() * 0.3f;
            float b = 0.7f + rand.nextFloat() * 0.2f;
            nodeColors.put(node, Color.getHSBColor(h, s, b));
            
            i++;
        }
        
        // 增加迭代次数以获得更好的布局
        for (int iter = 0; iter < 50; iter++) {
            for (String node : nodes) {
                Point pos = nodePositions.get(node);
                double forceX = 0, forceY = 0;
                
                // 斥力 (所有节点互相排斥)
                for (String otherNode : nodes) {
                    if (node.equals(otherNode)) continue;
                    
                    Point otherPos = nodePositions.get(otherNode);
                    double dx = pos.x - otherPos.x;
                    double dy = pos.y - otherPos.y;
                    double distance = Math.sqrt(dx * dx + dy * dy);
                    
                    if (distance < 1) distance = 1;
                    
                    // 大幅增加斥力系数，使节点之间分得更开
                    double repulsion = 3500.0 / (distance * distance);
                    forceX += repulsion * dx / distance;
                    forceY += repulsion * dy / distance;
                }
                
                // 引力 (相连节点互相吸引)
                Set<TextGraphProcessor.Edge> edges = graph.get(node);
                for (TextGraphProcessor.Edge edge : edges) {
                    String target = edge.target;
                    Point targetPos = nodePositions.get(target);
                    if (targetPos == null) continue;
                    
                    double dx = targetPos.x - pos.x;
                    double dy = targetPos.y - pos.y;
                    double distance = Math.sqrt(dx * dx + dy * dy);
                    
                    if (distance < 1) distance = 1;
                    
                    // 减小引力系数，使节点之间不会过于靠近
                    double attraction = distance / 60.0;
                    forceX += attraction * dx / distance;
                    forceY += attraction * dy / distance;
                }
                
                // 增加移动步长，使布局变化更明显
                double maxForce = 7.0;
                double forceMagnitude = Math.sqrt(forceX * forceX + forceY * forceY);
                if (forceMagnitude > maxForce) {
                    forceX = forceX / forceMagnitude * maxForce;
                    forceY = forceY / forceMagnitude * maxForce;
                }
                
                // 应用力并更新位置
                int newX = (int) (pos.x + forceX);
                int newY = (int) (pos.y + forceY);
                
                // 确保节点不会移出面板，留出较大边距
                int margin = NODE_RADIUS + 30;
                newX = Math.max(margin, Math.min(getWidth() - margin, newX));
                newY = Math.max(margin, Math.min(getHeight() - margin, newY));
                
                nodePositions.put(node, new Point(newX, newY));
            }
        }
        
        // 最后应用整体缩放，使图形更分散
        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;
        
        for (String node : nodes) {
            Point pos = nodePositions.get(node);
            int dx = pos.x - centerX;
            int dy = pos.y - centerY;
            
            int newX = centerX + (int)(dx * SCALE_FACTOR);
            int newY = centerY + (int)(dy * SCALE_FACTOR);
            
            // 再次确保在边界内，使用更宽松的限制
            int safeZone = 50; // 面板边缘安全区域
            if (newX < safeZone) newX = safeZone + rand.nextInt(50);
            if (newX > getWidth() - safeZone) newX = getWidth() - safeZone - rand.nextInt(50);
            if (newY < safeZone) newY = safeZone + rand.nextInt(50);
            if (newY > getHeight() - safeZone) newY = getHeight() - safeZone - rand.nextInt(50);
            
            nodePositions.put(node, new Point(newX, newY));
        }
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        
        // 启用抗锯齿
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        
        if (graph == null || graph.isEmpty() || nodePositions.isEmpty()) {
            g2d.setColor(Color.GRAY);
            g2d.drawString("无图数据或节点位置未计算", getWidth() / 2 - 80, getHeight() / 2);
            return;
        }
        
        // 首先绘制所有边
        for (String source : graph.keySet()) {
            Point sourcePos = nodePositions.get(source);
            if (sourcePos == null) continue;
            
            for (TextGraphProcessor.Edge edge : graph.get(source)) {
                Point targetPos = nodePositions.get(edge.target);
                if (targetPos == null) continue;
                
                // 绘制边和权重
                drawEdge(g2d, sourcePos, targetPos, edge.weight);
            }
        }
        
        // 然后绘制所有节点
        for (String node : graph.keySet()) {
            Point pos = nodePositions.get(node);
            if (pos == null) continue;
            
            // 绘制节点
            drawNode(g2d, pos, node);
        }
    }
    
    private void drawNode(Graphics2D g2d, Point pos, String label) {
        // 绘制节点圆形
        Color nodeColor = nodeColors.getOrDefault(label, NODE_COLOR);
        g2d.setColor(nodeColor);
        Ellipse2D.Double circle = new Ellipse2D.Double(pos.x - NODE_RADIUS, pos.y - NODE_RADIUS, 
                                                      2 * NODE_RADIUS, 2 * NODE_RADIUS);
        g2d.fill(circle);
        
        // 绘制边框
        g2d.setColor(nodeColor.darker());
        g2d.draw(circle);
        
        // 使用更大、更清晰的字体
        Font originalFont = g2d.getFont();
        Font boldFont = new Font(originalFont.getName(), Font.BOLD, 13);
        g2d.setFont(boldFont);
        
        // 绘制标签
        g2d.setColor(TEXT_COLOR);
        FontMetrics metrics = g2d.getFontMetrics();
        int textWidth = metrics.stringWidth(label);
        int textHeight = metrics.getHeight();
        g2d.drawString(label, pos.x - textWidth / 2, pos.y + textHeight / 4);
        
        // 恢复原来的字体
        g2d.setFont(originalFont);
    }
    
    private void drawEdge(Graphics2D g2d, Point source, Point target, int weight) {
        // 计算方向向量
        double dx = target.x - source.x;
        double dy = target.y - source.y;
        double length = Math.sqrt(dx * dx + dy * dy);
        
        // 归一化
        double unitDx = dx / length;
        double unitDy = dy / length;
        
        // 调整起点和终点，使边不与节点圆形重叠
        Point adjustedSource = new Point(
            (int) (source.x + unitDx * NODE_RADIUS),
            (int) (source.y + unitDy * NODE_RADIUS)
        );
        
        Point adjustedTarget = new Point(
            (int) (target.x - unitDx * NODE_RADIUS),
            (int) (target.y - unitDy * NODE_RADIUS)
        );
        
        // 绘制边
        g2d.setColor(EDGE_COLOR);
        g2d.setStroke(EDGE_STROKE);
        g2d.drawLine(adjustedSource.x, adjustedSource.y, adjustedTarget.x, adjustedTarget.y);
        
        // 绘制箭头
        drawArrow(g2d, adjustedSource, adjustedTarget);
        
        // 绘制权重，使用白色背景使其更明显
        int midX = (adjustedSource.x + adjustedTarget.x) / 2;
        int midY = (adjustedSource.y + adjustedTarget.y) / 2;
        String weightStr = Integer.toString(weight);
        
        // 添加背景使权重更容易阅读
        FontMetrics metrics = g2d.getFontMetrics();
        int textWidth = metrics.stringWidth(weightStr);
        int textHeight = metrics.getHeight();
        g2d.setColor(new Color(255, 255, 255, 220));
        g2d.fillRect(midX - textWidth/2 - 3, midY - textHeight/2, textWidth + 6, textHeight);
        
        g2d.setColor(Color.RED);
        g2d.drawString(weightStr, midX - textWidth/2, midY + textHeight/4);
    }
    
    private void drawArrow(Graphics2D g2d, Point from, Point to) {
        int arrowSize = 8; // 箭头大小
        
        // 计算方向向量
        double dx = to.x - from.x;
        double dy = to.y - from.y;
        double length = Math.sqrt(dx * dx + dy * dy);
        
        // 归一化
        double unitDx = dx / length;
        double unitDy = dy / length;
        
        // 计算箭头两个顶点
        int[] xPoints = new int[3];
        int[] yPoints = new int[3];
        
        // 箭头顶点
        xPoints[0] = to.x;
        yPoints[0] = to.y;
        
        // 箭头底边两个顶点
        double angle = Math.PI / 6; // 30度
        double backDX1 = unitDx * Math.cos(angle) - unitDy * Math.sin(angle);
        double backDY1 = unitDx * Math.sin(angle) + unitDy * Math.cos(angle);
        
        double backDX2 = unitDx * Math.cos(-angle) - unitDy * Math.sin(-angle);
        double backDY2 = unitDx * Math.sin(-angle) + unitDy * Math.cos(-angle);
        
        xPoints[1] = (int) (to.x - arrowSize * backDX1);
        yPoints[1] = (int) (to.y - arrowSize * backDY1);
        
        xPoints[2] = (int) (to.x - arrowSize * backDX2);
        yPoints[2] = (int) (to.y - arrowSize * backDY2);
        
        g2d.setColor(EDGE_COLOR);
        g2d.fillPolygon(xPoints, yPoints, 3);
    }
    
    @Override
    public Dimension getPreferredSize() {
        return new Dimension(1000, 800); // 增加面板的首选大小
    }
} 